<!-- 增删改查主表组件 2024.03.10 -->
<!-- 2024.09.30 改 -->

<template>
  <t-space direction="vertical" size="small" style="width: 100%">
    <t-space size="small" :breakLine="true">
      <t-button
        v-for="item in buttons"
        :key="item"
        :size="item.size || 'medium'"
        :icon="item.icon || undefined"
        :theme="item.theme || 'primary'"
        :disabled="item.disabled || false"
        @click="item.click"
      >
        {{ item.name }}
      </t-button>
      <t-button v-if="showRefresh" @click="handleRefreshData" theme="success"
        >刷新</t-button
      >
      <t-button v-if="showExport" @click="handleExportExcel">导出</t-button>
    </t-space>

    <t-space v-if="$slots.operations" size="small" style="width: 100%">
      <slot name="operations"></slot>
    </t-space>

    <t-loading :loading="loading" text="正在载入...">
      <t-enhanced-table
        :row-key="rowKey || 'id'"
        activeRowType="single"
        :data="tableData"
        :columns="tableColumns"
        :pagination="
          pagination
            ? {
                ...pagination,
                pageSizeOptions: [
                  5, 10, 20, 50, 100, 500, 1000, 2000, 5000, 10000,
                ],
              }
            : undefined
        "
        table-layout="fixed"
        :max-height="tableMaxHeight || '68vh'"
        :row-selection-type="rowSelectionType || 'multiple'"
        :select-on-row-click="selectOnRowClick || true"
        :row-selection-allow-uncheck="true"
        :stripe="stripe || false"
        @select-change="handleSelectChange"
        @filter-change="handleFilterChange"
        @page-change="handlePageChange"
        @row-dblclick="handleRowDbClick"
        :filter-value="filterValue"
        :tree="tableTreeOptions"
        :scroll="scroll || { type: 'virtual' }"
        tableContentWidth="100%"
        :rowspanAndColspan
        bordered
        lazy-load
      >
        <template v-if="$slots.rowOperations" #operation="{ row }">
          <slot name="rowOperations" :row="row"></slot>
        </template>
      </t-enhanced-table>
    </t-loading>
  </t-space>
</template>

<script lang="tsx" setup>
import { computed, toRaw, useSlots } from "vue";
import { MessagePlugin, type TableProps } from "tdesign-vue-next";

import dayjs from "dayjs";

import ExcelJS from "exceljs";
import streamSaver from "streamsaver";
import type { TFilterValue } from "@/types";

const slots = useSlots();

const props = defineProps([
  "mode",
  "loading",
  "buttons",
  "rowKey",
  "columns",
  "treeOptions",
  "tableData",
  "tableMaxHeight",
  "showOperations",
  "rowSelectionType",
  "scroll",
  "showExport",
  "multExport",
  "showRefresh",
  "tableStyleBorder",
  "exportTableConfig",
  "pagination",
  "filterValue",
  "hideEditInfo",
  "hideRemark",
  "hideRowSelect",
  "rowOpWidth",
  "rowspanAndColspan",
  "selectOnRowClick",
  "stripe",
]);

const emits = defineEmits([
  "currentChange",
  "filterChange",
  "refreshData",
  "pageChange",
  "rowDbClick",
]);

const tableColumns = computed(() => {
  const editInfoColumn: TableProps["columns"] = [
    {
      colKey: "createdTime",
      title: "创建时间",
      // sorter: true,
      width: 200,
      cell: (h, { row }) => {
        if (row.createdTime === null || row.createdTime === "") {
          return "没有记录";
        }
        return dayjs(row.createdTime).format("YYYY-MM-DD HH:mm:ss");
      },
    },
    {
      colKey: "createdByName",
      title: "创建人",
      width: 200,
    },
    {
      colKey: "updatedTime",
      title: "更新时间",
      width: 200,
      // sorter: true,
      cell: (h, { row }) => {
        if (row.updatedTime === null || row.updatedTime === "") {
          return "没有记录";
        }
        return dayjs(row.updatedTime).format("YYYY-MM-DD HH:mm:ss");
      },
    },
    {
      colKey: "updatedByName",
      title: "更新人",
      width: 200,
    },
  ];

  const remarkColumn: TableProps["columns"] = [
    {
      colKey: "remark",
      title: "备注",
      width: 450,
    },
  ];

  const rowOperationColumn: TableProps["columns"] = [
    {
      colKey: "operation",
      title: "操作",
      width: props.rowOpWidth || 150,
      fixed: "right",
    },
  ];

  const rowSelectColumn = [
    {
      colKey: "row-select",
      label: "选择",
      title: "选择",
      type: props.rowSelectionType || "multiple",
      width: 60,
      fixed: "left",
    },
  ];

  return [
    ...(props.hideRowSelect ? [] : rowSelectColumn),
    {
      colKey: "serial-number",
      title: "序号",
      align: "center",
      width: 70,
      fixed: "left",
    },
    ...props.columns,
    ...(props.hideEditInfo ? [] : editInfoColumn),
    ...(props.hideRemark ? [] : remarkColumn),
    ...(slots.rowOperations ? rowOperationColumn : []),
  ] as TableProps["columns"];
});

const tableTreeOptions = computed(() => {
  if (props.mode === "tree") {
    if (props.treeOptions) {
      return props.treeOptions;
    } else {
      return { childrenKey: "children", treeNodeColumnIndex: 2 };
    }
  } else {
    return undefined;
  }
});

const handleSelectChange = (
  keys: Array<Number | String>,
  options: Record<string, any>
) => {
  const emitData = {
    keys,
    options,
  };
  emits("currentChange", emitData);
};

const handleFilterChange = (
  filters: TFilterValue,
  ctx: Record<string, any>
) => {
  emits("filterChange", filters, ctx);
};

const handleRefreshData = () => {
  emits("refreshData");
};

const handleExportExcel = async () => {
  const workbook = new ExcelJS.Workbook();
  const sheet1 = workbook.addWorksheet("export") as any;
  let lastColumn: any = []
  if (tableColumns.value != null) {
    const tableColumnsValues = JSON.parse(JSON.stringify(tableColumns.value));
    // 判断是否多级导出
    if (props?.multExport == true) {
      lastColumn = multExportTable(tableColumnsValues, sheet1)
    } else {

      const headers = tableColumnsValues.map((item: any) => {
        return { header: item.title, key: item.colKey, width: item.width };
      });

      //设置表头
      sheet1.columns = headers.map((header: any) => ({
        header: header.header,
        key: header.key,
        width: header.width / 10 || 20,
      }));

      lastColumn = headers
    }
    //获取表格初始数据
    const sheetData = structuredClone(toRaw(props.tableData));

    //为数据增加序号
    sheetData.forEach((item: Record<string, any>, index: number) => {
      item["serial-number"] = index + 1;
    });

    //进行时间戳转换
    sheetData.forEach((item: Record<string, any>) => {
      if (item.createdTime !== null) {
        item.createdTime = dayjs(item.createdTime).format(
          "YYYY-MM-DD HH:mm:ss"
        );
      }
    });

    //对需要额外排序的数据进行排序
    if (Array.isArray(props.exportTableConfig)) {
      if (props.exportTableConfig.length > 0) {
        let tableKey: any;
        let valueKey: any;
        props.exportTableConfig.forEach((e: Record<string, any>) => {
          switch (e.mode) {
            case "getListLabel":
              tableKey = e.key;
              valueKey = e.valueKey;
              sheetData.forEach((item: Record<string, any>) => {
                let valueList: string = "";
                item[tableKey].forEach((value: any, index: number) => {
                  //判断该值是否为数组最后一个值，如果不是则加逗号分隔
                  if (index !== item[tableKey].length - 1) {
                    valueList += value[valueKey] + "，";
                  } else {
                    valueList += value[valueKey];
                  }
                });
                item[tableKey] = valueList;
              });
              break;
            case "toLabel":
              e.list.forEach((item: any) => {
                //该层循环获取用于修改数据的label和对应的value
                const label = item.label;
                const value = item.value;
                sheetData.forEach((item: Record<string, any>) => {
                  if (item[e.key] === value) {
                    item[e.key] = label;
                  }
                });
              });
              break;
          }
        });
      }
    }

    sheet1.addRows(sheetData);

    // 添加边框
    if (props?.tableStyleBorder == true) {
      for (let num = 0; num < sheet1.lastRow._number; num++) { // 循环出每一行
          for (let index = 0; index < lastColumn.length; index++) { // 循环出每一个单元格
              sheet1.getRow(num+1).getCell(index+1).border={ // 为单元格添加边框
                  top: {style:'thin'},
                  left: {style:'thin'},
                  bottom: {style:'thin'},
                  right: {style:'thin'}
              }
          }
      }
    }
    // 自动换行
    sheet1.eachRow({ includeEmpty: true }, (row: any, rowNumber: any) => {
        row.eachCell({ includeEmpty: true }, (cell: any) => {
          cell.alignment = { ...cell.alignment,wrapText: true, vertical: 'middle',
                horizontal: 'center' };
        });
    });
    

    //使用streamSaver保存表格至本地
    await workbook.xlsx
      .writeBuffer()
      .then((buffer) => {
        const file = new Blob([buffer], { type: "application/octet-stream" });
        const fileStream = streamSaver.createWriteStream("export.xlsx", {
          size: file.size, // Makes the percentage visiable in the download
        });
        const readableStream = file.stream();
        if (window.WritableStream && readableStream.pipeTo) {
          return readableStream.pipeTo(fileStream);
        }

        const writer = fileStream.getWriter();
        const reader = readableStream.getReader();
        const pump: any = () =>
          reader
            .read()
            .then((res) =>
              res.done ? writer.close() : writer.write(res.value).then(pump)
            );

        pump();
      })
      .catch((error) => MessagePlugin.error("导出失败", error));
  } else {
    MessagePlugin.error("导出失败");
  }
};

const handlePageChange = (
  pageInfo: Record<string, any>,
  newDataSource: Record<string, any>
) => {
  emits("pageChange", pageInfo, newDataSource);
};

const handleRowDbClick = (context: Record<string, any>) => {
  emits("rowDbClick", context);
};


// 递归算法
const traverseTree = (node: any) => {
  let arr: any = [];
  // 如果当前节点有子节点，递归遍历子节点
  if (node.children && node.children.length > 0) {
    node.children.forEach((child: any) => {
      arr.push(traverseTree(child));
    });
    node.children = [...arr];
  }
  return {
    header: node.title,
    key: node.colKey,
    width: node.width / 10 || 20,
    ...(node.style ? { style: node.style } : {}),
    ...(node.children ? { children: node.children } : {}),
  };
};
// 设置多级表头 - 深度搜索
const setHeader = (columnsHeader: any, data: any, deepNumber: any, y: any = 0) => {
        if (data.children) {
          deepNumber++;
          data.children.map((x: any, id: any) => {
            if (data.children[id - 1] && data.children[id - 1].children) {
              setHeader(columnsHeader, x, deepNumber, data.children[id-1]?.children.length - 1);
            } else { 
              setHeader(columnsHeader, x, deepNumber);
            }
          });
          const titleLength =
            columnsHeader[deepNumber - 1].length -
            JSON.parse(JSON.stringify(columnsHeader[deepNumber - 1]))
              .reverse()
              .findIndex((x: any) => x);
          columnsHeader[deepNumber - 1][titleLength + y] = data.header;
        } else {
          for (let i in columnsHeader) {
            if (i == deepNumber) {
              columnsHeader[i].push(data.header);
            } else {
              columnsHeader[i].push("");
            }
          }
        }
      };
// 扁平化
const flattTree = (data: any) => {
  let deepLength = 0;
  const columnsList: Array<{ header: string; key: string; width?: number; }> =
    [];
  const columnsStyleList: Array<{ header: string; key: string; width?: number; style?: any }> =
    [];
  const columnsHeader: any = [];

  // 计算深度的递归函数
  const func = ({ header, key, width, children, style }: any, depth: number) => {
    // 更新深度
    deepLength = Math.max(deepLength, depth);
    if (children) {
      columnsHeader.push([]);
      children.map((x: any) => {
        func(x, depth + 1); // 递归，深度加 1
      });
    } else {
      columnsList.push({ header, key, width });
    }
    columnsStyleList.push({ header, key, width, ...(style ? { style } : {}) });
  };

  // 遍历根节点，开始递归
  data.map((x: any) => {
    func(x, 1); // 根节点深度从 1 开始
  });
  return {
    columnsHeader: new Array(deepLength).fill(0).map(() => []),
    columnsList,
    deepLength,
    columnsStyleList
  };
};

// 多级表头导出
const multExportTable = (tableColumnsValue: any, sheet: any) => { 
  const letterMap: any = [
        "A",
        "B",
        "C",
        "D",
        "E",
        "F",
        "G",
        "H",
        "I",
        "J",
        "K",
        "L",
        "M",
        "N",
        "O",
        "P",
        "Q",
        "R",
        "S",
        "T",
        "U",
        "V",
        "W",
        "X",
        "Y",
        "Z",
      ];
  // 获取递归header
  const traverseHeaders = tableColumnsValue.map((item: any) =>
    traverseTree(item)
  );
  console.log(traverseHeaders)
  let { columnsHeader, columnsList, deepLength, columnsStyleList } =
    flattTree(traverseHeaders);
  sheet.columns = [...columnsList];
  traverseHeaders.map((x: any,id: any) => {
    let deepNumber = 0;
    if (traverseHeaders[id - 1] && traverseHeaders[id - 1].children) {
      setHeader(columnsHeader, x, deepNumber, traverseHeaders[id - 1].children.length - 1);
    } else { 
      setHeader(columnsHeader, x, deepNumber);
    }
  });
  columnsHeader.map((x, index) => {
    sheet.getRow(index + 1).values = x;
  });
  console.log(columnsHeader, 'columnsHeader', columnsList)
  columnsHeader.map((x, index) => {
  console.log(x, 'x')
    // 合并表头
    x.map((y, indexY) => {
      if (y) {
        // console.log(y, columnsStyleList.filter(((item: any) => item.header == y))[0].style)
        let style = columnsStyleList.filter(((item: any) => item.header == y))[0].style
        // 横向合并
        let endNumber = index;
        for (let i = index; i < deepLength; i++) {
          if (columnsHeader[i + 1] && columnsHeader[i + 1][indexY]) break;
          endNumber = i + 1;
        }
        if (endNumber - index >= 1) {
          try {
            // console.log(
            //   `${letterMap[indexY]}${index + 1}:${letterMap[indexY]}${endNumber + 1}`,
            //   "横向"
            // );
            sheet.mergeCells(
              `${letterMap[indexY]}${index + 1}:${letterMap[indexY]}${endNumber + 1}`
            );
            // sheet.getCell(`${letterMap[indexY]}${index + 1}`).alignment = {
            //   vertical: 'middle', horizontal: 'center'
            // };
            Object.assign(sheet.getCell(`${letterMap[indexY]}${index + 1}`), {
              alignment: {
                vertical: 'middle',
                horizontal: 'center'
              },
              ...(style ? { ...style } : {})
            });
          } catch (error) {
            // console.log(error);
          }
        }
        // 纵向合并
        let rightNumber = indexY;
        for (let i = rightNumber + 1; i < x.length; i++) {
          if (x[i]) break;
          rightNumber = i;
        }
        if (rightNumber - indexY >= 1) {
          try {
            sheet.mergeCells(
              `${letterMap[indexY]}${index + 1}:${letterMap[rightNumber]}${index + 1}`
            ); 
            // sheet.getCell(`${letterMap[indexY]}${index + 1}`).alignment = {
            //   vertical: 'middle',
            //   horizontal: 'center'
            // };
            Object.assign(sheet.getCell(`${letterMap[indexY]}${index + 1}`), {
              alignment: {
                vertical: 'middle',
                horizontal: 'center'
              },
              ...(style ? { ...style } : {})
            });
          } catch (error) {
            // console.log(error);
          }
        }
      }
    });
  });
  return columnsList;
}

</script>
